package com.koron.device.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.koron.bean.base.Response;
import com.koron.bean.enums.EquipmentParam;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.common.core.business.datadict.utils.DataDictTool;
import com.koron.common.core.business.workflow.util.TextUtil;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.util.*;
import com.koron.device.bean.Equipment;
import com.koron.device.bean.EquipmentLocation;
import com.koron.device.bean.EquipmentLocationQuery;
import com.koron.device.bean.EquipmentParams;
import com.koron.device.bean.template.LocationErrorMsg;
import com.koron.device.bean.template.LocationTemplate;
import com.koron.device.enums.CsysType;
import com.koron.device.mapper.EquipmentLocationMapper;
import com.koron.device.service.EquipmentLocationService;
import com.koron.device.service.EquipmentService;
import com.koron.device.service.FeignService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备管理-位置管理{@code EquipmentLocationServiceImpl} 服务实现层
 *
 * <p></p>
 *
 * @ClassName EquipmentLocationServiceImpl
 * @description
 * @Author 朱超
 * @Date 2021/7/23
 */
@Service
@Transactional
@Slf4j
public class EquipmentLocationServiceImpl implements EquipmentLocationService {

    private EamUser getCurrUser() {
        return ThreadLocalContext.get();
    }

    @Autowired
    private EquipmentService equipmentService;

    @Autowired
    private FeignService feignService;

    @Override
    @TaskAnnotation("checkBeforeInsert")
    public Response checkBeforeInsert(SessionFactory factory, EquipmentLocation equipmentLocation) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        String code = equipmentLocation.getCode();
        equipmentLocation.setId(CodeTools.getCode32());
        equipmentLocation.setNamePy(HanyupinyinUtil.getPinyinString(equipmentLocation.getName()));

        // 1. 设置地理位置系统编码
        if (StringUtils.isEmpty(code)) {
            // 1.1 如果用户没填地理位置系统编码，则后台自动生成
            equipmentLocation.setCode(CodeTools.getCode32());
        } else {
            // 1.2 确保用户填写的地理位置系统编码与现有的不重复
            EquipmentLocation uniqueCode = equipmentLocationMapper.getByCode(code);
            if (uniqueCode != null) {
                return Response.fail("地理位置系统编码不可重复");
            }
        }
        if (ObjectUtil.isNotNull(equipmentLocation.getCsysType())) {
            String parseLatLon = parseLatLon(equipmentLocation.getEnterLatLon(), CsysType.valueOf(equipmentLocation.getCsysType()));
            equipmentLocation.setLatlon(parseLatLon);
        }
        // 2. 保证同一父级下的兄弟节点名称不重复
//        EquipmentLocationQuery uniqueName = new EquipmentLocationQuery();
//        uniqueName.setName(equipmentLocation.getName());
//        uniqueName.setParentId(equipmentLocation.getParentId());
        List<EquipmentLocation> nameCheckList = equipmentLocationMapper.queryByNameAndParentIdAndDeleteFlag(equipmentLocation.getName(), equipmentLocation.getParentId(), 0);
        if (nameCheckList.size() != 0) {
            return Response.fail("同一父级下的兄弟节点名称不重复");
        }
        // 3. 父级层级 < 子节点层级
        if (equipmentLocation.getParentLevel() > equipmentLocation.getLevel()) {
            return Response.fail("父级层级应小于等于子节点层级");
        }
        // 4. 设置层级编码
        Integer maxLevelCode = equipmentLocationMapper.getMaxLevelCodeByLevelPath(equipmentLocation.getLevelPath());
        maxLevelCode = maxLevelCode == null ? 0 : maxLevelCode + 1;
        equipmentLocation.setLevelCode(maxLevelCode);
        // 5. 设置层级路径
        if ("0".equals(equipmentLocation.getParentId())) {
            equipmentLocation.setLevelPath(maxLevelCode + "");
            equipmentLocation.setFullPathCode(equipmentLocation.getId());
            equipmentLocation.setFullPathName(equipmentLocation.getName());
        } else {
            EquipmentLocation parent = equipmentLocationMapper.getById(equipmentLocation.getParentId());
            //String levelPath = equipmentLocationMapper.getLevelPathById(equipmentLocation.getParentId());
            equipmentLocation.setLevelPath(parent.getLevelPath() + "/" + maxLevelCode);
            equipmentLocation.setFullPathCode(parent.getFullPathCode() + "/" + equipmentLocation.getId());
            equipmentLocation.setFullPathName(parent.getFullPathName() + "/" + equipmentLocation.getName());
        }
        // 6. 设置排序
        Integer currentParentMaxSort = equipmentLocationMapper.getCurrentParentMaxSort(equipmentLocation.getParentId());
        currentParentMaxSort = currentParentMaxSort == null ? 1 : currentParentMaxSort;
        equipmentLocation.setSort(currentParentMaxSort + 1);
        // 7. 设置删除标志，id, 创建人id/姓名，创建时间
        equipmentLocation.setDeleteFlag(0);
        equipmentLocation.setCreateUserId(getCurrUser().getId());
        equipmentLocation.setCreateUserName(getCurrUser().getName());
        equipmentLocation.setCreateTime(new Date());
        // 8. 插入数据
        equipmentLocationMapper.insert(equipmentLocation);

        return Response.success(equipmentLocation);
    }

    @Override
    @TaskAnnotation("logicDelete")
    public Response logicDelete(SessionFactory factory, String id) {
        EquipmentLocation equipmentLocation = new EquipmentLocation();
        equipmentLocation.setId(id);
        equipmentLocation.setDeleteFlag(1);
        EamUser eamUser = ThreadLocalContext.get();
        equipmentLocation.setUpdateUserId(eamUser.getId());
        equipmentLocation.setUpdateTime(new Date());
        equipmentLocation.setUpdateUserName(eamUser.getUpdateByName());
        factory.getMapper(EquipmentLocationMapper.class).update(equipmentLocation);
        return Response.success("删除成功");
    }

    @Override
    @TaskAnnotation("queryList")
    public Response queryList(SessionFactory factory, EquipmentLocationQuery query) {
        PageHelper.startPage(query.getPage(), query.getPageSize());
        EquipmentLocationMapper mapper = factory.getMapper(EquipmentLocationMapper.class);
        return Response.ok(new PageInfo<>(mapper.getList(query)));
    }

    @Override
    @TaskAnnotation("checkBeforeUpdate")
    public Response checkBeforeUpdate(SessionFactory factory, EquipmentLocation equipmentLocation) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        String id = equipmentLocation.getId();
        String code = equipmentLocation.getCode();
        String name = equipmentLocation.getName();
        equipmentLocation.setNamePy(HanyupinyinUtil.getPinyinString(name));

        // 1. 地理位置系统编码，判重
        if (!StringUtils.isEmpty(code)) {
            EquipmentLocation checkCode = equipmentLocationMapper.getByCode(code);
            if (checkCode != null && !id.equals(checkCode.getId())) {
                return Response.fail("地理位置系统编码不能重复");
            }
        }
        // 2. 地理位置名称，判重
        String parentId = equipmentLocation.getParentId();
        EquipmentLocation checkName = equipmentLocationMapper.getByNameAndParentId(name, parentId);
        if (checkName != null && !id.equals(checkName.getId())) {
            return Response.fail("同一节点下兄弟节点名称不能重复");
        }
        if (ObjectUtil.isNotNull(equipmentLocation.getCsysType())) {
            String parseLatLon = parseLatLon(equipmentLocation.getEnterLatLon(), CsysType.valueOf(equipmentLocation.getCsysType()));
            equipmentLocation.setLatlon(parseLatLon);
        }
        // 3. 更新的相关信息
        equipmentLocation.setUpdateUserId(getCurrUser().getId());
        equipmentLocation.setUpdateUserName(getCurrUser().getName());
        equipmentLocation.setUpdateTime(new Date());
        // 4. 修改数据
        equipmentLocationMapper.update(equipmentLocation);
        EquipmentLocation parent = equipmentLocationMapper.getById(parentId);
        if (!Objects.isNull(parent)) {
            equipmentLocation.setFullPathName(parent.getFullPathName() + "/" + equipmentLocation.getName());
        }
        return Response.success("修改成功");
    }

    @Override
    @TaskAnnotation("getTree")
    public Response getTree(SessionFactory factory, String status) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        EquipmentLocationQuery equipmentLocationQuery = new EquipmentLocationQuery();
        equipmentLocationQuery.setStatus(status);
        // 1. 先查出所有数据
        List<EquipmentLocation> equipmentLocationList = equipmentLocationMapper.getList(equipmentLocationQuery);
        // 2. 根据parentId聚合数据
        Map<String, List<EquipmentLocation>> parentMap = equipmentLocationList.stream().collect(Collectors.groupingBy(EquipmentLocation::getParentId));
        for (String parentId : parentMap.keySet()) {
            // 3. 将子节点塞到父节点数据中
            List<EquipmentLocation> parentList = parentMap.get(parentId);
            for (EquipmentLocation parentBean : parentList) {
                parentBean.setChildren(parentMap.get(parentBean.getId()));
            }
        }
        List<EquipmentLocation> result = parentMap.get("0");
        return Response.success(result);
    }

    /**
     * @param factory
     * @param id       当前被移动节点的id
     * @param targetId 目标节点的id
     * @param sort     移动到目标的子节点时，sort为空，移动到兄弟节点时，sort为目标的位置(非目标节点的位置，是将要移到的位置)
     * @return
     */
    @Override
    @TaskAnnotation("moveNode")
    public Response moveNode(SessionFactory factory, String id, String targetId, Integer sort) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        // 1. 当前节点
        EquipmentLocation equipmentLocation = equipmentLocationMapper.getById(id);
        if (sort == null) {
            // 移动到目标节点的子级，则目标节点为父节点
            EquipmentLocationQuery equipmentLocationQuery = new EquipmentLocationQuery();
            equipmentLocationQuery.setParentId(targetId);
            List<EquipmentLocation> equipmentLocationList = equipmentLocationMapper.getList(equipmentLocationQuery);
            String nameListString = equipmentLocationList.stream().map(EquipmentLocation::getName).collect(Collectors.joining(","));
            if (nameListString.contains(equipmentLocation.getName())) {
                return Response.fail("该节点位置名称与新的父节点下数据位置名称有冲突！！！");
            }
            // 5. 更新节点及其子节点的树信息
            updateTree(factory, targetId, equipmentLocation);
        } else {
            // 移动到目标节点的同级，则目标节点为兄弟节点
            // 如果更新的位置
            List<EquipmentLocation> locations = new ArrayList<>();
            EquipmentLocation target = equipmentLocationMapper.getById(targetId);
            EquipmentLocation parent = equipmentLocationMapper.getById(target.getParentId());
            String parentLevelPath = parent == null ? "" : parent.getLevelPath() + "/";
            // 目标节点和当前节点同父
            equipmentLocation.setParentId(target.getParentId());
            equipmentLocation.setParentName(target.getParentName());
            equipmentLocation.setSort(sort == 0 ? 1 : sort);
            equipmentLocation.setLevel(target.getLevel());
            equipmentLocation.setLevelCode(equipmentLocation.getSort());
            // 兄弟节点的level_path、full_path_code、full_path_name只有最后一段不同
            equipmentLocation.setLevelPath(parentLevelPath + equipmentLocation.getLevelCode());
            equipmentLocation.setFullPathCode((parent == null ? "" : parent.getFullPathCode() + "/") + equipmentLocation.getId());
            equipmentLocation.setFullPathName((parent == null ? "" : parent.getFullPathName() + "/") + equipmentLocation.getName());
            // 目标节点后面的所有兄弟节点，都往后移一位，所有子节点的level_path需要更新
            List<EquipmentLocation> brotherNodes = equipmentLocationMapper.getBrotherNode(equipmentLocation.getParentId(), sort);
            brotherNodes.remove(equipmentLocation);
            // 未修改时，需要修改的兄弟节点的level_path, key: value ===> 替换前的level_path: 替换后的level_path
            List<Map<String, String>> list = new ArrayList<>();
            for (EquipmentLocation location : brotherNodes) {
                Map<String, String> levelPathMap = new HashMap<>();
                location.setLevelCode(location.getLevelCode() + 1);
                levelPathMap.put("oldLevelPath", location.getLevelPath());
                list.add(levelPathMap);
                location.setLevelPath(parentLevelPath + location.getLevelCode());
                levelPathMap.put("newLevelPath", location.getLevelPath());
                location.setSort(location.getSort() + 1);
            }
            locations.addAll(brotherNodes);
            locations.add(equipmentLocation);
            for (EquipmentLocation location : locations) {
                equipmentLocationMapper.update(location);
            }
            if (CollectionUtils.isNotEmpty(list)) {
                equipmentLocationMapper.replaceLevelPath(list);
            }
        }
        return Response.success("节点移动成功");
    }

    @Override
    @TaskAnnotation("initializeFirst")
    public Response initializeFirst(SessionFactory factory) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        String url = "https://safetyorg.gdhwater.com/org/getDepartments.htm?orgCode=9FACED5B258442C78735C4AE2D63703FLXQQXB0HQ1KJG2P4KXD4Q4ZSGQSB0R8G&departmentCode=d29fdd2bc8a4474d8b17d7f69c96569e&recursive=1";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpGet httpGet = new HttpGet(url);
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity entity = httpResponse.getEntity();
            String responseString = EntityUtils.toString(entity);
            Map<String, String> responseMap = TextUtil.toMapFromString(responseString);
            Map<String, String> listMap = TextUtil.toMapFromString(responseMap.get("data"));
            List<JSONObject> dataMapList = TextUtil.toListMapFromString(listMap.get("list"));
            for (JSONObject dataMap : dataMapList) {
                EquipmentLocation equipmentLocation = new EquipmentLocation();
                equipmentLocation.setId((String) dataMap.get("departmentCode"));
                equipmentLocation.setCode("0");
                equipmentLocation.setName((String) dataMap.get("name"));
                equipmentLocation.setParentId("0");
                equipmentLocation.setParentName("0");
                equipmentLocation.setLevel(0);
                equipmentLocation.setLevelCode(0);
                equipmentLocation.setLevelPath("0");
                equipmentLocation.setStatus("enabled");
                equipmentLocation.setDeleteFlag(0);
                equipmentLocationMapper.insertBak(equipmentLocation);
            }
        } catch (ClientProtocolException e) {
            log.error(e.getMessage());
        } catch (IOException e) {
            log.error(e.getMessage());
        }

        return Response.success();
    }

    @Override
    @TaskAnnotation("initializeSecond")
    public Response initializeSecond(SessionFactory factory) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);

        List<EquipmentLocation> equipmentLocationList = equipmentLocationMapper.getListBak();
        for (EquipmentLocation equipmentLocation : equipmentLocationList) {
            String id = equipmentLocation.getId();
            String parentId = equipmentLocationMapper.getParentIdById(id);
//            Integer sort = equipmentLocationMapper.getSortById(id);
            equipmentLocation.setParentId(parentId);
//            equipmentLocation.setSort(sort);
            equipmentLocationMapper.updateBak(equipmentLocation);
        }
        return Response.success("更新成功!");
    }

    @Override
    @TaskAnnotation("initializeTwoPointFive")
    public Response initializeTwoPointFive(SessionFactory factory) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);

        List<EquipmentLocation> equipmentLocationList = equipmentLocationMapper.getListBak();
        for (EquipmentLocation equipmentLocation : equipmentLocationList) {
            String name = equipmentLocation.getName();
            Integer sort = feignService.getSortByName(name);
            equipmentLocation.setSort(sort);
            equipmentLocationMapper.updateBak(equipmentLocation);
        }
        return Response.success("更新成功!");
    }

    @Override
    @TaskAnnotation("refreshLatLon")
    public Response refreshLatLon(SessionFactory factory) {
        EquipmentLocationMapper locationMapper = factory.getMapper(EquipmentLocationMapper.class);
        List<EquipmentLocation> list = locationMapper.getList(new EquipmentLocationQuery());
        list.stream().forEach(row -> {
            String parseLatLon = parseLatLon(row.getEnterLatLon(), CsysType.key(row.getCsysType()));
            row.setLatlon(parseLatLon);
            locationMapper.update(row);
        });
        return Response.ok();
    }

    @Override
    @TaskAnnotation("getRangeLocation")
    public Response getRangeLocation(SessionFactory factory, EquipmentLocationQuery query) {
        Map<String, String> dataDictByCode = DataDictTool.getDataDictByCode("publicDictionary", "range_threshold");
        Double range = Double.parseDouble(dataDictByCode.get("device_near_range"));
        // 获取设备参数
        Map<String, EquipmentParams> genericParams = equipmentService.getGenericParam(factory, query.getEquipmentId());
        // 获取设备定位id
        EquipmentParams equipmentParams = genericParams.get(EquipmentParam.LOCATION.key());
        String locationId = equipmentParams.getParamResult();
        EquipmentLocationMapper mapper = factory.getMapper(EquipmentLocationMapper.class);
        // 当前设备的定位
        EquipmentLocation location = mapper.getById(locationId);
        String currLatlon = location.getLatlon();
        Assert.isTrue(StringUtils.contains(currLatlon, ",") && StringUtils.split(currLatlon, ",").length == 2, "设备没有设置经纬度");
        // 获取所有启用且有经纬度的定位
        List<EquipmentLocation> locationEquipment = mapper.getRangeLocation(query);
        // 附近的定位
        List<EquipmentLocation> rangeEquipment = locationEquipment.stream().filter(row -> {
            String latlon = row.getLatlon();
            if (!StringUtils.contains(latlon, ",") && StringUtils.split(latlon, ",").length != 2) {
                return false;
            }
            String lon = StringUtils.split(latlon, ",")[0];
            String lat = StringUtils.split(latlon, ",")[1];
            return DistanceUtils.getDistance(Double.parseDouble(lon), Double.parseDouble(lat),
                    Double.parseDouble(StringUtils.split(currLatlon, ",")[0]), Double.parseDouble(StringUtils.split(currLatlon, ",")[1])) <= range;
        }).collect(Collectors.toList());
        return Response.ok(rangeEquipment);
    }

    @Override
    @TaskAnnotation("getLeftUpperLocationByOrgId")
    public Response getLeftUpperLocationByOrgId(SessionFactory factory, String orgId) {
        EquipmentLocationMapper mapper = factory.getMapper(EquipmentLocationMapper.class);
        return Response.ok(mapper.getLeftUpperLocation(orgId));
    }

    @Override
    @TaskAnnotation("initializeThird")
    public Response initializeThird(SessionFactory factory) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        String url = "https://safety.gdhwater.com/GridNode/list.htm?corpCode=d29fdd2bc8a4474d8b17d7f69c96569e";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpGet httpGet = new HttpGet(url);
            HttpResponse httpResponse = httpClient.execute(httpGet);
            HttpEntity entity = httpResponse.getEntity();
            String responseString = EntityUtils.toString(entity);
            Map<String, String> responseMap = TextUtil.toMapFromString(responseString);
            List<JSONObject> dataMapList = TextUtil.toListMapFromString(responseMap.get("data"));
            for (JSONObject dataMap : dataMapList) {
                // 1. 来自于安全系统的数据字段
                EquipmentLocation equipmentLocation = new EquipmentLocation();
                equipmentLocation.setId((String) dataMap.get("id"));
                equipmentLocation.setName((String) dataMap.get("name"));
                equipmentLocation.setCreateUserName((String) dataMap.get("addUserName"));
                Date createTime = new Date((Long) dataMap.get("addTime"));
                equipmentLocation.setCreateTime(createTime);
                equipmentLocation.setUpdateUserName((String) dataMap.get("updateUserName"));
                equipmentLocation.setSort((Integer) dataMap.get("serial"));
                if (dataMap.get("updateTime") != null) {
                    Date updateTime = new Date((Long) dataMap.get("updateTime"));
                    equipmentLocation.setUpdateTime(updateTime);
                }
                equipmentLocation.setDesc((String) dataMap.get("remark"));
                // 2. 表中的数据字典
                equipmentLocation.setCode((String) dataMap.get("code"));
                String parentId = (String) dataMap.get("pid");
                String parentName = "0";
                equipmentLocation.setParentId(parentId);
                // 3.2 parentName 第一遍拿不到
                equipmentLocation.setParentName(parentName);
                // 3.3 第一遍将level相关的数据都设置成0
                equipmentLocation.setLevel(0);
                equipmentLocation.setLevelCode(0);
                equipmentLocation.setLevelPath("0");
                equipmentLocation.setStatus("enabled");
                equipmentLocation.setDeleteFlag(0);
                equipmentLocationMapper.insertBak(equipmentLocation);
            }
        } catch (ClientProtocolException e) {
            log.error(e.getMessage());
        } catch (IOException e) {
            log.error(e.getMessage());
        }

        return Response.success();
    }

    @Override
    @TaskAnnotation("initializeFourth")
    public Response initializeFourth(SessionFactory factory) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        EquipmentLocationQuery equipmentLocationQuery = new EquipmentLocationQuery();
        // 1. 先查出所有数据
        List<EquipmentLocation> equipmentLocationList = equipmentLocationMapper.getList(equipmentLocationQuery);
        // 2. 根据parentId聚合数据
        Map<String, List<EquipmentLocation>> parentMap = equipmentLocationList.stream().collect(Collectors.groupingBy(EquipmentLocation::getParentId));
        for (String parentId : parentMap.keySet()) {
            if (!parentId.equals("0")) {
                List<EquipmentLocation> parentList = parentMap.get(parentId);
                for (EquipmentLocation parentBean : parentList) {
                    this.moveNode(factory, parentBean.getId(), parentId, null);
                }
            }
        }

        return Response.success("初始化成功");
    }

    private void updateTree(SessionFactory factory, String parentId, EquipmentLocation equipmentLocation) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        EquipmentLocation parentLocation = equipmentLocationMapper.getById(parentId);
        String id = equipmentLocation.getId();
        // 1. 设置parentId、parentName、levelCode
        equipmentLocation.setParentId(parentId);
        equipmentLocation.setParentName(parentLocation.getName());
        equipmentLocation.setLevel(parentLocation.getLevel() + 1);
        // 查询父节点下，最大的level_code,如果没有，则从1开始，如果有，在最大的基础上+1
        Integer maxLevelCode = equipmentLocationMapper.getMaxLevelCodeByLevelPath(parentLocation.getLevelPath());
        maxLevelCode = maxLevelCode == null ? 1 : maxLevelCode + 1;
        equipmentLocation.setLevelCode(maxLevelCode);
        equipmentLocation.setSort(maxLevelCode);
        // 4. 设置levelPath、fullPathCode、fullPathName
        if ("0".equals(parentId)) {
            equipmentLocation.setLevelPath(maxLevelCode + "");
            equipmentLocation.setFullPathCode(equipmentLocation.getId());
            equipmentLocation.setFullPathName(equipmentLocation.getName());
        } else {
            equipmentLocation.setLevelPath(parentLocation.getLevelPath() + "/" + maxLevelCode);
            equipmentLocation.setFullPathCode(parentLocation.getFullPathCode() + "/" + equipmentLocation.getId());
            equipmentLocation.setFullPathName(parentLocation.getFullPathName() + "/" + equipmentLocation.getName());
        }
        // 5. 更新数据本身
        equipmentLocationMapper.update(equipmentLocation);
        // 6. 更新数据的子节点相关信息
        EquipmentLocationQuery equipmentLocationQuery = new EquipmentLocationQuery();
        equipmentLocationQuery.setParentIdChildLike(id);
        // 根据full_path_code查询所有的子节点
        List<EquipmentLocation> children = equipmentLocationMapper.getList(equipmentLocationQuery);
        if (CollectionUtils.isEmpty(children)) {
            return;
        }
        for (EquipmentLocation child : children) {
            updateTree(factory, id, child);
        }
    }

    @Override
    @TaskAnnotation("getById")
    public Response getById(SessionFactory factory, String id) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        // 1. 查出基本数据
        EquipmentLocation equipmentLocation = equipmentLocationMapper.getById(id);
        // 2. 查出位置关联设备
        /*List<Equipment> equipmentList = equipmentService.getEquipmentByLocation(factory, id);
        if(CollectionUtils.isNotEmpty(equipmentList)) {
            equipmentList = equipmentList.stream().filter(row -> row.getDeleteFlag() != null && 0 == row.getDeleteFlag()).collect(Collectors.toList());
        }
        equipmentLocation.setEquipmentList(equipmentList);*/
        return Response.success(equipmentLocation);
    }

    @Override
    @TaskAnnotation("getEquipmentById")
    public Response getEquipmentById(SessionFactory factory, String locationId) {
        List<Equipment> equipmentList = equipmentService.getEquipmentByLocation(factory, locationId);
        return Response.ok(new PageInfo<>(equipmentList));
    }

    @Override
    @TaskAnnotation("getByIdWithoutEquipment")
    public Response getByIdWithoutEquipment(SessionFactory factory, List<String> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Response.ok();
        }
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        // 1. 查出基本数据
        List<EquipmentLocation> locations = equipmentLocationMapper.getBatchByIds(ids);
        return Response.success(locations);
    }

    @Override
    @TaskAnnotation("getByEquipId")
    public Response getByEquipId(SessionFactory factory, String equipId) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        return Response.success(equipmentLocationMapper.getByEquipId(equipId));
    }

    @Override
    @TaskAnnotation("getParentIdByOrgId")
    public Response getParentIdByOrgId(SessionFactory factory, String orgId) {
        EquipmentLocationMapper equipmentLocationMapper = factory.getMapper(EquipmentLocationMapper.class);
        List<String> result = new ArrayList<>();

        // 1. 获取关联组织数据中最上一级的level
        Integer minLevel = equipmentLocationMapper.getMinLevelByOrgId(orgId);
        // 2. 获取关联组织数据中最上一级的level_code
        Integer minLevelCode = equipmentLocationMapper.getMinLevelCodeByOrgIdAndLevel(orgId, minLevel);
        // 3. 根据level和level_code获得对应数据的level_path
        String levelPath = equipmentLocationMapper.getLevelPathByLevelAndLevelCode(minLevel, minLevelCode);
        // 4. 将levelPath中对应的数据的id返回
        String[] levelCodeList = levelPath.split("/");
        for (int i = 0; i < levelCodeList.length; i++) {
            // i对应了数据中的level，而list[i]对应着对应level的levelCode，这两条数据可以定位到一个id
            String id = equipmentLocationMapper.getIdByLevelAndLevelCode(i, Integer.parseInt(levelCodeList[i]));
            result.add(id);
        }

        return Response.success(result);
    }

    private String parseLatLon(String latLon, CsysType csysType) {
        if (StringUtils.isBlank(latLon) || csysType == null) {
            return latLon;
        }
        String[] split = StringUtils.split(latLon, ",");
        // 经纬度要么为空，要么逗号分隔
        Assert.isTrue(StringUtils.isBlank(latLon) || ArrayUtils.getLength(split) == 2, "经纬度格式不合法");
        Assert.isTrue(StringUtils.isBlank(latLon) || csysType != null, "定位方式不能为空");
        switch (csysType) {
            case GPS: {
                double[] numberic = LatLonUtil.gps84_To_Gcj02(Double.parseDouble(split[1]), Double.parseDouble(split[0]));
                return numberic[1] + "," + numberic[0];
            }
            case BD09: {
                double[] numberic = LatLonUtil.bd09_To_Gcj02(Double.parseDouble(split[1]), Double.parseDouble(split[0]));
                return numberic[1] + "," + numberic[0];
            }
            case GCJ02:
                // 默认的定位类型
                return latLon;
            default:
                break;
        }
        return latLon;
    }

    @Override
    @TaskAnnotation("export")
    public List<LocationTemplate> export(SessionFactory factory, String id) {

        EquipmentLocationMapper mapper = factory.getMapper(EquipmentLocationMapper.class);
        List<EquipmentLocation> list = mapper.queryByFullPathCodeLike(id);// 空查全部
        Map<String, String> idCodemap = list.stream().collect(Collectors.toMap(EquipmentLocation::getId, EquipmentLocation::getCode));
        List<LocationTemplate> telelist = new ArrayList();
        list.stream().forEach(row -> {
            LocationTemplate tele = new LocationTemplate();
            tele.setParentCode(idCodemap.get(row.getParentId()));//父位置编码
            tele.setName(row.getName());
            tele.setCode(row.getCode());
            tele.setYdu(StrUtil.split(Opt.ofBlankAble(row.getEnterLatLon()).orElse(","), ",").get(0));// 前面是经度
            tele.setXdu(StrUtil.split(Opt.ofBlankAble(row.getEnterLatLon()).orElse(","), ",").get(1));// 后面是纬度
            tele.setRange(row.getRange());
            tele.setStatusStr(row.getStatus().equals("enabled") ? "启用" : "停用");
            tele.setOrgName(row.getOrgName());
            tele.setDesc(row.getDesc());
            telelist.add(tele);
        });

        return telelist;

    }

    @Override
    @TaskAnnotation("searchByNameList")
    public List<EquipmentLocation> searchByNameList(SessionFactory factory, List<String> nameList) {
        EquipmentLocationMapper mapper = factory.getMapper(EquipmentLocationMapper.class);
        return mapper.selectByNameList(nameList);
    }

    /**
     * 导入数据校验
     *
     * @param factory
     * @param importlist  导入数据
     * @param codeNamemap 位置编码、名称map
     * @return
     */
    @TaskAnnotation("checkLocation")
    public List<LocationErrorMsg> checkLocation(SessionFactory factory, List<LocationTemplate> importlist, Map<String, String> codeNamemap) {
        Set<String> dbcodes = codeNamemap.keySet();//数据库所有编码
        List<String> impCodelist = importlist.stream().map(LocationTemplate::getCode).distinct().collect(Collectors.toList());//用于检验表格父级是否存在于表格
        Map<String, List<LocationTemplate>> codeGroupBymap = importlist.stream().filter(f->StrUtil.isNotBlank(f.getName())).collect(Collectors.groupingBy(LocationTemplate::getCode));//用于检验表格重复位置编码

        // 导入数据的层级编码集合
        List<LocationErrorMsg> errorlist = new ArrayList();
        for (LocationTemplate tele : importlist) {

            if (StrUtil.isBlank(tele.getName()) && StrUtil.isBlank(tele.getCode())) {
                continue;
            }

            if (StrUtil.isBlank(tele.getName())) {
                errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("位置名称不能为空"));
                continue;
            }

            if (StrUtil.isBlank(tele.getCode())) {
                errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("位置编码不能为空"));
                continue;
            } else {
                if (dbcodes.contains(tele.getCode())) {
                    String msg = StrUtil.format("位置编码重复.与现系统位置：{} 编码重复", codeNamemap.get(tele.getCode()));
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg(msg));
                    continue;
                }
                if (codeGroupBymap.get(tele.getCode()).size() >= 2) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("表格存在重复位置编码"));
                    continue;
                }
            }

            if (StrUtil.isNotBlank(tele.getParentCode())) {
                // 位置编码在表格的父级位置编码列找不到 && 父级编码在数据库编码找不到
                if (!impCodelist.contains(tele.getParentCode()) && !dbcodes.contains(tele.getParentCode())) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("无法找父级编码"));
                    continue;
                } else {
                    String parentName = codeNamemap.get(tele.getParentCode());
                    if (StrUtil.isNotBlank(parentName)) {
                        tele.setParentName(parentName);//父级节点在数据库
                    } else {
                        tele.setParentName(codeGroupBymap.get(tele.getParentCode()).get(0).getName());//父级节点在表格
                    }
                }
            }

            if (StrUtil.isBlank(tele.getYdu())) {
                errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("经度(°)不能为空"));
                continue;
            } else {
                Double checkbig = new Double(tele.getYdu());
                if (NumberUtil.compare(checkbig, 180) == 1) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("经度(°)不能大于180"));
                    continue;
                }
                if (NumberUtil.compare(-180, checkbig) == 1) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("经度(°)不能小于-180"));
                    continue;
                }
            }

            if (StrUtil.isBlank(tele.getXdu())) {
                errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("纬度(°)不能为空"));
                continue;
            } else {
                Double checkbig = new Double(tele.getXdu());
                if (NumberUtil.compare(checkbig, 90) == 1) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("纬度(°)不能大于90"));
                    continue;
                }
                if (NumberUtil.compare(-90, checkbig) == 1) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("纬度(°)不能小于-90"));
                    continue;
                }
            }

            tele.setEnterLatLon(StrUtil.join(",", tele.getYdu(), tele.getXdu()));//补充录入经纬度

            if (ObjectUtil.isNull(tele.getRange())) {
                errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("有效范围不能为空"));
                continue;
            } else {
                Double checkbig = new Double(tele.getRange());
                if (NumberUtil.compare(checkbig, 1000) == 1) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("有效范围不能大于1000"));
                    continue;
                }
                if (NumberUtil.compare(0, checkbig) == 1) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("纬度(°)不能小于0"));
                    continue;
                }
            }

            if (StrUtil.isNotBlank(tele.getCsysTypeStr())) {
                if (CsysType.keyStr(tele.getCsysTypeStr()) == null) {
                    errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("坐标类型无法识别"));
                    continue;
                } else {
                    String key = CsysType.keyStr(tele.getCsysTypeStr()).getKey();
                    key = key == "WGS84" ? "GPS" : key;
                    tele.setCsysType(key);
                }
            }


            if (!StrUtil.equalsAny(tele.getStatusStr(), "启用", "停用")) {
                errorlist.add(Convert.convert(LocationErrorMsg.class, tele).setErrorMsg("状态无法识别"));
                continue;
            }

            errorlist.add(Convert.convert(LocationErrorMsg.class, tele));
        }
        return errorlist;
    }

    /**
     * 位置构建入库
     *
     * @param factory
     * @param checklist 表格导入数据
     * @param parentLo  父级节点
     * @return
     */
    @TaskAnnotation("buildLocation")
    public void buildLocation(SessionFactory factory, List<LocationErrorMsg> checklist, EquipmentLocation parentLo, List<EquipmentLocation> allLocationlist) {
        EquipmentLocationMapper mapper = factory.getMapper(EquipmentLocationMapper.class);
        List<EquipmentLocation> alllocation = mapper.queryAll();
        // 所有位置编码
        Map<String, String> allcodeIdmap = alllocation.stream().collect(Collectors.toMap(EquipmentLocation::getCode, EquipmentLocation::getId));
        Map<String, String> idNamemap = allLocationlist.stream().collect(Collectors.toMap(EquipmentLocation::getId, EquipmentLocation::getName));
        //导入数据转换成位置数据格式数据
        List<EquipmentLocation> locationlist = new ArrayList();
        for (LocationErrorMsg imp : checklist) {
            EquipmentLocation location = Convert.convert(EquipmentLocation.class, imp);
            location.setId(CodeTools.getCode32());
            // 如果有父节点，在数据校验时候已经赋值， 如果是null 则代表是顶级节点
            if (StrUtil.isBlank(location.getParentName())) {
                location.setParentName("0");
            }
            if (ObjectUtil.isNotNull(imp.getCsysType())) {
                String parseLatLon = parseLatLon(imp.getEnterLatLon(), CsysType.valueOf(imp.getCsysType()));
                location.setLatlon(parseLatLon);
            }
            location.setCreateUserId(getCurrUser().getAccount());
            location.setCreateUserName(getCurrUser().getName());
            location.setCreateTime(new Date());
            location.setDeleteFlag(0);
            location.setStatus(imp.getStatusStr().equals("启用") ? "enabled" : "disabled");
            location.setDataSources(StrUtil.format("{}导入", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm")));
            location.setSort(Integer.valueOf(imp.getRowNum()));
            locationlist.add(location);
        }
        //根据带入的层级编码 和 生成id 组成map, 用匹配出自身的父级
        Map<String, String> levelPathmap = locationlist.stream().collect(Collectors.toMap(EquipmentLocation::getCode, EquipmentLocation::getId));

        // 匹配父级
        for (EquipmentLocation location : locationlist) {
            // 数据库是否存在父级编码
            String existParentCode = allcodeIdmap.get(location.getParentCode());
            if (StrUtil.isNotBlank(location.getParentCode())) {
                if (parentLo != null && location.getParentCode().equals(parentLo.getCode())){
                    location.setParentId(parentLo.getId());
                } else if(existParentCode != null){
                    location.setParentId(existParentCode);
                }else{
                    location.setParentId(levelPathmap.get(location.getParentCode()));
                }
            } else {
                if (parentLo != null) {
                    location.setParentId(parentLo.getId());
                } else {
                    location.setParentId("0");
                }
            }
        }
        String parentId = "";
        if (parentLo != null) {
            locationlist.add(parentLo);
            parentId = parentLo.getId();
        } else {
            parentId = "0";
        }

        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        treeNodeConfig.setWeightKey("sort");
        treeNodeConfig.setIdKey("id");
//        // 检查表格父节点是否只有一个
//        List<String> collect = locationlist.stream().map(EquipmentLocation::getParentId).collect(Collectors.toList());


        //转换器 注意这个 topNode.getId() 为 最顶级节点 id。 后面枝叶都依据此扩展
//        List<Tree<String>> treeList = TreeUtil.build(dblist, "03000000000001", treeNodeConfig,
        List<Tree<String>> treeList = TreeUtil.build(locationlist, parentId, treeNodeConfig,
                (treeNode, node) -> {
                    node.setId(treeNode.getId());
                    node.setParentId(treeNode.getParentId());
                    node.setWeight(treeNode.getSort());    //排序权重
                    node.setName(treeNode.getName());
                    // 扩展属性 ...
//                    node.putExtra("id", treeNode.getId());
//                    node.putExtra("dispatchNo", treeNode.getDispatchNo());
//                    node.putExtra("dataFrom", treeNode.getDataFrom());

                    // 如果不想一个个putExtra，可以直接Bean 转 Map 遍历赋值
                    Map<String, Object> map = BeanUtil.beanToMap(treeNode);
                    for (Map.Entry<String, Object> entry : map.entrySet()) {
                        if (StrUtil.equalsAny(entry.getKey(), "id", "parentId")) {
                            continue;
                        }
                        node.putExtra(entry.getKey(), entry.getValue());
                    }

                });

        // 把树形结果 通过 json 转换 成 集合  ，这种适合做数据通过表格 数据处理 插库
        List<EquipmentLocation> jsonToList = FastJsonUtils.getJsonToList(FastJsonUtils.getBeanToJson(treeList), EquipmentLocation.class);

        List<EquipmentLocation> insertlist = new ArrayList();
        treeLocationToList(jsonToList, insertlist);//递归成一层的结构

        Map<String, EquipmentLocation> typeMap = insertlist.stream().collect(Collectors.toMap(EquipmentLocation::getId, item -> item));
        if (parentLo != null) {
            typeMap.put(parentLo.getId(), parentLo);
            configDeviceLevel(insertlist, typeMap, parentLo.getParentId());
        } else {
            configDeviceLevel(insertlist, typeMap, "0");
        }

        insertlist.removeIf(e -> e.getId().equals(Opt.ofNullable(parentLo).map(EquipmentLocation::getParentId).orElse("0")));//插入时候移除父节点id,如果没有父节点也不影响

        Lists.partition(insertlist, 300).stream().forEach(p -> {
            mapper.batchInsert(p);
        });
    }

    /**
     * 位置递归 变更 一层
     *
     * @param treeVos
     * @param vos
     */
    private static void treeLocationToList(List<EquipmentLocation> treeVos, List<EquipmentLocation> vos) {
        for (int i = 0; i < treeVos.size(); i++) {
            vos.add(treeVos.get(i));
            if (!CollectionUtils.isEmpty(treeVos.get(i).getChildren())) {
                treeLocationToList(treeVos.get(i).getChildren(), vos);
            }
        }
    }

    private static void configDeviceLevel(List<EquipmentLocation> vos, Map<String, EquipmentLocation> typeMap, String parentId) {
        Iterator<EquipmentLocation> iterator = vos.iterator();
        int k = 0;
        while (iterator.hasNext()) {
            EquipmentLocation vo = iterator.next();
            if (!StringUtils.equals(vo.getParentId(), parentId)) {
                if (!typeMap.containsKey(vo.getParentId())) {
                    continue;
                }
                // 如果有父节点
                EquipmentLocation parent = typeMap.get(vo.getParentId());
                vo.setParentName(parent.getName());
                vo.setLevel(parent.getLevel() + 1);
                vo.setLevelPath(parent.getLevelPath() + "/" + (k + 1));
                vo.setFullPathName(parent.getFullPathName() + "/" + vo.getName());
                vo.setFullPathCode(parent.getFullPathCode() + "/" + vo.getId());
            } else {
                vo.setLevel(1);
                vo.setLevelPath(String.valueOf(k + 1));
                vo.setFullPathName(vo.getName());
                vo.setFullPathCode(vo.getId());
            }
            k++; // TODO 位置？
            vo.setLevelCode((k + 1));
            vo.setSort((k + 1));
            if (!CollectionUtils.isEmpty(vo.getChildren())) {
                List<EquipmentLocation> children = vo.getChildren();
                Collections.sort(children, Comparator.comparingInt(EquipmentLocation::getSort));
                vo.setChildren(children);
                configDeviceLevel(vo.getChildren(), typeMap, parentId);
            }
        }
    }
}
